package com.heyi.nettylong.server.handler;

import com.alibaba.fastjson.JSONObject;
import com.heyi.nettylong.common.protobuf.Command;
import com.heyi.nettylong.common.protobuf.Message;
import com.heyi.nettylong.common.util.MessageUtil;
import com.heyi.nettylong.common.util.SpringBeanFactory;
import com.heyi.nettylong.server.listener.ChannelDataListener;
import com.heyi.nettylong.server.typeServer.NettyServer;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.net.InetSocketAddress;
import java.util.Random;

/**
  * 实现心跳的hander  支持超时断开客户端避免浪费资源
**/
@ChannelHandler.Sharable
public class ServerHeartHandler extends ChannelInboundHandlerAdapter {
	public Logger log = LoggerFactory.getLogger(this.getClass());

	private final AttributeKey<String> clientInfo = AttributeKey.valueOf("clientInfo");
	private final ChannelDataListener channelDataListener = SpringBeanFactory.getBean(ChannelDataListener.class);

	// 定义没有收到客户端的ping消息的最大次数
	private final int MAX_UN_REC_PING_TIMES = 2;

	// 失败计数器：未收到client端发送的ping请求
	private int unRecPingTimes = 0 ;

	/*空闲触发器 心跳基于空闲实现*/
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) {
			IdleStateEvent event = (IdleStateEvent) evt;
			if (event.state() == IdleState.READER_IDLE) {

//				if(unRecPingTimes >= MAX_UN_REC_PING_TIMES){
					// 连续超过N次未收到client的ping消息，那么关闭该通道，等待client重连
					String clientId = ctx.attr(clientInfo).get().split(",")[0];
					NettyServer.channelRepository.remove(clientId);
					ctx.channel().close();
					log.info("客户端[{},{}]心跳超时,关闭并移除连接", clientId, ctx.channel().remoteAddress());
					if(channelDataListener != null) {
						channelDataListener.disconnected(clientId, new Throwable("客户端心跳超时"));
					}
//				}else{
//					// 失败计数器加1
//					unRecPingTimes++;
//				}
			}
		}else {
			super.userEventTriggered(ctx, evt);
		}
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		Message.MessageBase msgBase = (Message.MessageBase)msg;
		String clientId = msgBase.getClientId();

		/*认证处理*/
		if(msgBase.getCmd().equals(Command.CommandType.AUTH)){
			Channel channel = NettyServer.channelRepository.get(clientId);
			// 判断是否活跃
			if(channel != null){
				InetSocketAddress ctxsocket = (InetSocketAddress) ctx.channel().remoteAddress();
				InetSocketAddress chlsocket = (InetSocketAddress) channel.remoteAddress();
				if(chlsocket == null || ctxsocket.getAddress().getHostAddress().equals(chlsocket.getAddress().getHostAddress())) {
					channel.close();
					log.info("客户端[{}]已存在，清除旧连接", clientId);
				}else {
					log.info("客户端[{}]已在其它设备登录，拒绝连接", clientId);
					ctx.writeAndFlush(MessageUtil.build(clientId, Command.CommandType.AUTH_BACK, "["+clientId+"]已在其它设备登录")).addListener((ChannelFuture futureListener)->{
						ctx.channel().close();
					});
					return;
				}
			}
			if(channelDataListener != null) {
				JSONObject jsonObject = JSONObject.parseObject(msgBase.getData());
				if(channelDataListener.authority(clientId, jsonObject.getLong("timestamp"), jsonObject.getString("sing"))) {
					String token = getRandomString(8);
					ctx.attr(clientInfo).set(clientId+","+token);
					NettyServer.channelRepository.put(clientId, ctx.channel());
					ctx.writeAndFlush(MessageUtil.build(clientId, Command.CommandType.AUTH_BACK, "success", token));
				}else {
					ctx.writeAndFlush(MessageUtil.build(clientId, Command.CommandType.AUTH_BACK, "权限验证没通过")).addListener((ChannelFuture futureListener)->{
						ctx.channel().close();
					});
				}
			}
		}else if(msgBase.getCmd().equals(Command.CommandType.PING)){
			log.debug("收到心跳[{}]", clientId);
			String token = ctx.attr(clientInfo).get().split(",")[1];
			if(StringUtils.isEmpty(token) || !token.equals(msgBase.getToken())){
				log.info("客户端[{}]token错误，拒绝连接", clientId);
				ctx.writeAndFlush(MessageUtil.build(clientId, Command.CommandType.AUTH_BACK, "权限验证没通过")).addListener((ChannelFuture futureListener)->{
					ctx.channel().close();
				});
			}else {
				ctx.writeAndFlush(MessageUtil.build(clientId, Command.CommandType.PONG, "PONG[" + clientId + "]"));
				unRecPingTimes = 0;
			}
		}else{
			if(ctx.channel().isOpen()){
				//触发下一个handler
				ctx.fireChannelRead(msg);
			}else {
				log.info("接收到其它类型[{}]且channel is not open:{}", clientId, msgBase.getCmd());
			}
		}
		// 释放msg数据
		ReferenceCountUtil.release(msg);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

		String clientId = ctx.attr(clientInfo).get().split(",")[0];
		log.info("客户端[{}},{}]失连关闭.{}", clientId, ctx.channel().remoteAddress(), cause.getMessage());
		NettyServer.channelRepository.remove(clientId);
		ctx.channel().close();
		if(channelDataListener != null) {
			channelDataListener.disconnected(clientId, cause);
		}
	}

	//length用户要求产生字符串的长度
	public static String getRandomString(int length){
		String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		Random random=new Random();
		StringBuffer sb=new StringBuffer();
		for(int i=0;i<length;i++){
			int number=random.nextInt(62);
			sb.append(str.charAt(number));
		}
		return sb.toString();
	}
}
